
#include <BOpenGL>
#include <BRender>
#include <BSurface>
#include "member_BSurface.h"

using namespace BWE;

#define member					(*(member_BSurface*)_ptr)
#define member_allocate()		_ptr = new member_BSurface(this)
#define member_release()		delete (member_BSurface*)_ptr

BSurface::BSurface(const BString& name) : BShape(name)
{
	member_allocate();
}
BSurface::BSurface(BReal width, BReal height, int horSegment, int verSegment)
{
	member_allocate();
	member.quad.a().set(0.0f, 0.0f, 0.0f);
	member.quad.b().set(width, 0.0f, 0.0f);
	member.quad.c().set(width, height, 0.0f);
	member.quad.d().set(0.0f, height, 0.0f);
	member.horizontalSegment = horSegment;
	member.verticalSegment = verSegment;
	member.dirty = true;
	this->flush();
}
BSurface::~BSurface()
{
	member_release();
}

void BSurface::copy(const BShape& other)
{
	BShape::copy(other);
}
BShape* BSurface::clone() const
{
	return new BSurface(*this);
}

void BSurface::setQuadSize(BReal width, BReal height)
{
	member.quad.a().set(0.0f, 0.0f, 0.0f);
	member.quad.b().set(width, 0.0f, 0.0f);
	member.quad.c().set(width, height, 0.0f);
	member.quad.d().set(0.0f, height, 0.0f);
	member.dirty = true;
	this->dirty();
}
void BSurface::setQuadEdges(const BVector& veca, const BVector& vecb, const BVector& vecc, const BVector& vecd)
{
	BQuad quad(veca, vecb, vecc, vecd);
	setQuadEdges(quad);
}
void BSurface::setQuadEdges(const BQuad& quad)
{
	if (member.quad != quad)
	{
		member.quad = quad;
		this->dirty();
	}
}
BQuad BSurface::quadEdges() const
{
	return member.quad;
}

void BSurface::setEdges(const BVectorArray& edges)
{
	member.edges = edges;
	member.dirty = true;
	this->dirty();
}
const BVectorArray& BSurface::edges() const
{
	return member.edges;
}
BVectorArray& BSurface::edges()
{
	return member.edges;
}

void BSurface::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		this->dirty();
	}
}
Orientation BSurface::orientation() const
{
	return member.orientation;
}

void BSurface::setVerticalSegment(int verticalSegment)
{
	if (member.verticalSegment != verticalSegment)
	{
		member.verticalSegment = verticalSegment;
		this->dirty();
	}
}
int BSurface::verticalSegment() const
{
	return member.verticalSegment;
}

void BSurface::setHorizontalSegment(int horizontalSegment)
{
	if (member.horizontalSegment != horizontalSegment)
	{
		member.horizontalSegment = horizontalSegment;
		this->dirty();
	}
}
int BSurface::horizontalSegment() const
{
	return member.horizontalSegment;
}

void BSurface::setVertices(BVectorArray* vertices)
{
	if (member.vertices && member.vertices->size() != vertices->size())
		return;
	member.vertices = vertices;
	this->dirty();
}
const BVectorArray* BSurface::vertices() const
{
	return member.vertices;
}
BVectorArray* BSurface::vertices()
{
	return member.vertices;
}

void BSurface::setNormals(BVectorArray* normals)
{
	if (member.normals && member.normals->size() != normals->size())
		return;
	member.normals = normals;
	this->fresh();
}
const BVectorArray* BSurface::normals() const
{
	return member.normals;
}
BVectorArray* BSurface::normals()
{
	return member.normals;
}

void BSurface::setColors(BColorArray* colors)
{
	if (member.colors && member.colors->size() != colors->size())
		return;
	member.colors = colors;
	this->fresh();
}
const BColorArray* BSurface::colors() const
{
	return member.colors;
}
BColorArray* BSurface::colors()
{
	return member.colors;
}

void BSurface::setTexCoords(BCoordArray* texCoords, int unit)
{
	if (member.vertices && member.vertices->size() != texCoords->size())
		return;
	if (unit < 0 || unit >= 8)
		return;
	member.coords = texCoords;
	if(this->texture(0))
		this->fresh();
}
const BCoordArray* BSurface::texCoords(int unit) const
{
	return member.coords;
}
BCoordArray* BSurface::texCoords(int unit)
{
	return member.coords;
}

void BSurface::setIndices(BIndexArray* indices)
{
	member.indices = indices;
	this->fresh();
}
const BIndexArray* BSurface::indices() const
{
	return member.indices;
}
BIndexArray* BSurface::indices()
{
	return member.indices;
}

void BSurface::clear()
{
	member.vertices = 0;
	member.normals = 0;
	member.colors = 0;
	member.indices = 0;
	member.coords = 0;
	this->dirty();
}

bool BSurface::empty() const
{
	return member.vertices.empty();
}
void BSurface::smooth()
{
	BVectorArray* vertices = member.vertices;
	BVectorArray* normals = member.normals;
	if (!normals || normals->size() != vertices->size())
	{
		normals = new BVectorArray(vertices->size());
		member.normals = normals;
	}
	normals->fill(BVector());
	BIndexArray* indices = member.indices;
	if (indices)
	{
		int size = indices->size();
		for (int i = 0; i < size; i += 4)
		{
			unsigned int ia = (*indices)[i + 0];
			unsigned int ib = (*indices)[i + 1];
			unsigned int ic = (*indices)[i + 2];
			unsigned int id = (*indices)[i + 3];
			const BVector& va = (*vertices)[ia];
			const BVector& vb = (*vertices)[ib];
			const BVector& vc = (*vertices)[ic];
			BVector normal = (vb - va).cross(vc - vb);
			normal.normalize();
			(*normals)[ia] += normal;
			(*normals)[ib] += normal;
			(*normals)[ic] += normal;
			(*normals)[id] += normal;
		}
	}
	for (int i = 0; i < normals->size(); i++)
	{
		(*normals)[i].normalize();
	}
	emit(Signal_Fresh);
}
void BSurface::reverse()
{
	BIndexArray* indices = member.indices;
	for (int i = 0; i < indices->size(); i += 4)
	{
		unsigned int& ib = (*indices)[i + 1];
		unsigned int& id = (*indices)[i + 3];
		bSwap(ib, id);
	}
	BVectorArray* normals = member.normals;
	if (!normals || normals->size() != this->vertices()->size())
	{
		smooth();
		return;
	}
	for (int i = 0; i < normals->size(); i++)
	{
		(*normals)[i] = -(*normals)[i];
	}
}


void BSurface::refresh(BBox& box)
{
	if (member.orientation == Orientation_Horizontal)
		member.rebuildHorizontal();
	if (member.orientation == Orientation_Vertical)
		member.rebuildVertical();
	BSpace space;
	if (member.vertices && member.vertices->size())
	{
		space.reset(member.vertices->first());
		for (int i = 1; i < member.vertices->size(); i++)
		{
			space.expand((*member.vertices.ptr())[i]);
		}
	}
	box = space;
}
void BSurface::render(BRender& render)
{
	if (!member.vertices || member.vertices->empty() || member.indices.empty())
		return;
	if (member.vertices)
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		BOpenGL::glVertexArray(member.vertices);
	}
	if (member.normals)
	{
		glEnableClientState(GL_NORMAL_ARRAY);
		BOpenGL::glNormalArray(member.normals);
	}
	if (member.colors)
	{
		glEnableClientState(GL_COLOR_ARRAY);
		glColorPointer(4, GL_UNSIGNED_BYTE, 0, member.colors->data());
	}
	if (member.coords && this->texture(0))
	{
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, 0, member.coords->data());
	}
	if (member.indices)
	{
		glEnableClientState(GL_INDEX_ARRAY);
		glDrawElements(GL_QUADS, member.indices->size(), GL_UNSIGNED_INT, member.indices->data());
		glDisableClientState(GL_INDEX_ARRAY);
	}
	if (member.vertices)
	{
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	if (member.normals)
	{
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	if (member.colors)
	{
		glDisableClientState(GL_COLOR_ARRAY);
	}
	if (member.coords && this->texture(0))
	{
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
}

